﻿namespace VividKit.ServiceModel
{
    using System;
    using System.Diagnostics;
    using System.Threading.Tasks;

    public class TraceQueryDispatcher : IQueryDispatcher
    {
        private readonly TraceSource log;
        private readonly IQueryDispatcher queryDispatcher;

        public TraceQueryDispatcher(IQueryDispatcher queryDispatcher)
        {
            this.queryDispatcher = queryDispatcher;
            this.log = new TraceSource(this.queryDispatcher.GetType().Name, SourceLevels.Information);
        }

        public TResult Run<TResult>(IQuery<TResult> query)
        {
            var stopwatch = new Stopwatch();
            try
            {
                this.log.TraceEvent(TraceEventType.Information, 0, "Running {0}", query.GetType().Name);
                this.log.TraceObject(TraceEventType.Verbose, 0, query);
                stopwatch.Start();

                return this.queryDispatcher.Run(query);
            }
            catch (Exception x)
            {
                this.log.TraceData(TraceEventType.Warning, 0, x);
                throw;
            }
            finally
            {
                stopwatch.Stop();
                this.log.TraceEvent(TraceEventType.Information, 0, "Ran {0} ({1} ms)",
                    query.GetType().Name, stopwatch.ElapsedMilliseconds);
            }
        }

        public async Task<TResult> RunAsync<TResult>(IQuery<TResult> query)
        {
            var stopwatch = new Stopwatch();
            try
            {
                this.log.TraceEvent(TraceEventType.Information, 0, "Running {0}", query.GetType().Name);
                this.log.TraceObject(TraceEventType.Verbose, 0, query);
                stopwatch.Start();

                return await this.queryDispatcher.RunAsync(query);
            }
            catch (Exception x)
            {
                this.log.TraceData(TraceEventType.Warning, 0, x);
                throw;
            }
            finally
            {
                stopwatch.Stop();
                this.log.TraceEvent(TraceEventType.Information, 0, "Ran {0} ({1} ms)",
                    query.GetType().Name, stopwatch.ElapsedMilliseconds);
            }
        }
    }
}